import { RUNNING, createUUID, statusName } from '../constants';
import BehaviorTree from './BehaviorTree';
import Blackboard from './Blackboard';
import Tick from './Tick';

const on_enter = '->enter'
const on_open = '->open'
const on_tick = '->tick'
const on_close = '->close'
const on_exit = '->exit'
const is_open = 'isOpen'

export default abstract class BaseNode {

  readonly id: string
  readonly name: string
  readonly properties: Map<string, any> = new Map()
  debug: boolean = false

  constructor(name: string) {
    this.id = createUUID();
    this.name = name;
  }

  addProperties(key: string, value: any) {
    this.properties.set(key, value)
    return this
  }

  removeProperties(key: string) {
    return this.properties.delete(key)
  }

  hasProperties(key: string) {
    return this.properties.has(key)
  }

  execute(tick: Tick, ...args: any[]): number {
    // ENTER
    tick.enterNode(this);
    this.onEnter(tick, ...args);
    let debugPath = on_enter

    // OPEN
    if (!tick.blackboard.get(is_open, tick.tree.id, this.id)) {
      tick.openNode(this);
      tick.blackboard.set(is_open, true, tick.tree.id, this.id);
      this.onOpen(tick, ...args);
      debugPath += on_open
    }

    // TICK
    tick.tickNode(this);
    const status = this.onTick(tick, ...args);
    debugPath += `${on_tick}[${statusName(status)}]`

    // CLOSE
    if (status !== RUNNING) {
      tick.closeNode(this);
      tick.blackboard.set(is_open, false, tick.tree.id, this.id);
      this.onClose(tick, ...args);
      debugPath += on_close
    }

    // EXIT
    tick.exitNode(this, status);
    this.onExit(tick, status, ...args);
    debugPath += on_exit

    // DEBUG
    if (this.debug) {
      console.log(this.name, debugPath)
    }
    return status;
  }

  onEnter(tick: Tick, ...args: any[]) { }

  onOpen(tick: Tick, ...args: any[]) { }

  abstract onTick(tick: Tick, ...args: any[]): number

  onClose(tick: Tick, ...args: any[]) { }

  onExit(tick: Tick, status: number, ...args: any[]) { }

  createTree(blackboard = new Blackboard()): BehaviorTree {
    return new BehaviorTree(this, blackboard)
  }
};
